/*
尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理.

5. 书写完基本的代码后，需要执行异步代码
6. 同一个promise对象，多次执行then方法，
如果是同步的函数，那么当时执行then的时候，就执行了
如果是异步函数，那么就需要等到异步函数执行完，才能执行then里面的回调函数，
此时，如果有多个promise.then调用，那么此时，可能有多个回调函数等待执行
7. then方法可以链式调用，后面的then的参数是前一个then的回调的返回值
 链式调用；前一个then的回调的返回值(需要区分普通值和promise对象), 传递给下一个then的回调
8. 自己返回自己的情况，将报错信息传递下去
9. 捕获错误，并且传递下去
 执行器里面的错误，直接在constructor里面捕获
 then里面的错误，也捕获到，传递给下一个then
10. then可选参数
    promise.then(value => value).then(value => value).then(() => {}, () => {})
11. Promise.all(['普通值', 异步方法]).then(value => {
静态方法
    value => [] 对应参数的顺序
})
12. Promise.resovle， 将传入的值（如果是普通值，就转换，如果是promise，那么就直接返回）转换为 promise对象
13. finally方法，不管成功还是失败，都会调用；finally之后，可以.then
14. catch方法，失败的回调
*/


const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
    constructor(exceutor) {
        try {
            exceutor(this.resolve, this.reject)
        }catch (e) {
            this.reject(e.message)
        }
    }
    // 状态
    status = PENDING
    // 成功的值
    value = undefined
    // 失败的值
    reason = undefined
    successCallback = []
    failCallback = []
    resolve = (value) => {
        // status一旦改变，无法修改
        if(this.status !== PENDING) return
        this.status = FULFILLED
        this.value = value
        // 执行这个函数时，就代表可以执行回调（如果调用then时）
        while(this.successCallback.length) this.successCallback.shift()()
        // this.successCallback && this.successCallback(this.value)
    }
    reject = (reason) => {
        if(this.status !== PENDING) return
        this.status = REJECTED
        this.reason = reason
        while(this.failCallback.length) this.failCallback.shift()()
        // this.failCallback && this.failCallback(this.reason)
    }
    then (successCallback, failCallback) {
        successCallback = successCallback ? successCallback: value => value
        failCallback = failCallback ? failCallback: reason => { throw reason }

        let promise = new MyPromise((resolve, reject) => {
            // 判断状态
            if(this.status === FULFILLED) {
                setTimeout(() => {
                    try {
                        let x = successCallback(this.value)
                        // resolve(x)
                        // 注意这里如果是同步代码，这个promise是获取不到的，所以转换成异步代码就可以了
                        resolvePromise(promise,x, resolve,reject)
                    }catch (e) {
                        reject(e)
                    }
                }, 0)
            } else if (this.status === REJECTED) {
                setTimeout(() => {
                    try {
                        let x = failCallback(this.reason)
                        // 注意这里如果是同步代码，这个promise是获取不到的，所以转换成异步代码就可以了
                        resolvePromise(promise,x, resolve,reject)
                    }catch (e) {
                        reject(e)
                    }
                }, 0)

            } else {
                //如果执行器里面是异步代码时，这里状态还没有改变，所以不能马上执行回调函数
                // 这里是pending,即主执行程序中有异步代码
                // 将回调函数保存起来，等待执行了resolve/reject之后，再执行
                this.successCallback.push(() => {
                    setTimeout(() => {
                        try {
                            let x = successCallback(this.value)
                            // resolve(x)
                            // 注意这里如果是同步代码，这个promise是获取不到的，所以转换成异步代码就可以了
                            resolvePromise(promise,x, resolve,reject)
                        }catch (e) {
                            reject(e)
                        }
                    }, 0)
                })
                this.failCallback.push(() => {
                    setTimeout(() => {
                        try {
                            let x = failCallback(this.reason)
                            // 注意这里如果是同步代码，这个promise是获取不到的，所以转换成异步代码就可以了
                            resolvePromise(promise,x, resolve,reject)
                        }catch (e) {
                            reject(e)
                        }
                    }, 0)
                })
            }
        })

        // 返回promise就可以实现链式调用
        return promise
    }
    finally(callback) {// 最终返回promise
        // 最终都会执行，通过then方法知道 状态 =》 来callback
        return this.then(value => {
            // 需要处理返回值为promise的情况
            return MyPromise.resolve(callback()).then(() => value)
            // callback()
            // return value // 将值传递给下一个then的回调
        }, reason => {
            return MyPromise.resolve(callback()).then(() => { throw reason })
            // callback()
            // throw reason
        })
    }
    catch(callback) { // catch回调，最终可以调用then
        return this.then(undefined, callback)
    }
    static all(arr) {
        // 需要返回promise,后续可以调用then方法

        return new MyPromise((resolve, reject) => {
            let result = []
            let index= 0
            function addData(key, value) {
                result[key] = value
                index++
                if (index === arr.length){
                    // 所有的异步方法都执行完之后，才能调用resolve
                    resolve(result)
                }
            }
            for(let i =0;i< arr.length; i++) {
                if(arr[i] instanceof MyPromise) {
                    // promise对象
                    let current = arr[i]
                    // 此处异步代码，在for循环里面， 有一个失败，就失败了
                    current.then(value => addData(i, value), reason => reject(reason))
                } else {
                    // 普通值
                    addData(i, arr[i])
                }
            }
            // 不能放这里，因为result中还有异步，没有执行完得到结果的回调，如果直接在这里resolve,那么结果就位空
            // resolve(result)
        })
    }
    static resolve(value) {
        if(value instanceof MyPromise) {
            // promise
            return value
        } else {
            // 普通值
            return new MyPromise((resolve, reject)=> {
                resolve(value)
            })
        }
    }

}
/*
* 如果x为普通值，那么直接调用resolve/reject
* 如果x 为promise对象，将x的回调函数的值传递给 resolve/reject
*
* */
function resolvePromise(promise, x, resolve,reject) {
    if(promise === x) {
        return reject(new TypeError('promise循环调用了'))
    }
    if(x instanceof MyPromise) {
        // x为promise
        x.then(resolve, reject)
    } else {
        resolve(x)
    }
}
module.exports = MyPromise
